package com.lobov.utils;

import com.lobov.entities.ContainerOfSnips;
import com.lobov.entities.Matrix;
import com.lobov.entities.Snip;

/**
 * 
 * Утилита для резки матриц по количеству процессоров
 */

public class Cutter {
	/**
	 * горизонтальная резка матрицы
	 * 
	 * @param matrix
	 *            подаваемая на разкройку матрица
	 * @param numOfProcessors
	 *            количество обрабатываемых процессоров
	 * @return возвращает контейнер лоскутов ContainerOfSnips
	 * @throws Exception 
	 */
	public static ContainerOfSnips cutMatrixHorizontal(Matrix matrix,
			int numOfProcessors) throws Exception {
		if (matrix.isCompressed()){
			throw new Exception("Нельзя разрезать сжатую матрицу");
		}
		int snipXlength = matrix.getXlength();
		ContainerOfSnips containerOfSnips = new ContainerOfSnips(numOfProcessors, snipXlength);
		Snip[] sps = containerOfSnips.getSnips(); 
		int counter = 0;
		int numMatrixOfLines = matrix.getYlength();
		int snipYlengthMin = numMatrixOfLines / numOfProcessors;
		int snipYlengthMax = numMatrixOfLines / numOfProcessors + numMatrixOfLines % numOfProcessors;
		int numOfSnips = numOfProcessors;
		int numOfStartElement = 0;
		if (snipYlengthMin == snipYlengthMax) { //сли матрица поделена на равные лоскуты
			for (int i = 0; i < numOfSnips; i++) {
				sps[counter] = getSnipOfMatrix(matrix, numOfStartElement, snipXlength,
						snipYlengthMin, counter);
				numOfStartElement += snipXlength * snipYlengthMin;
				counter++;
			}
		} else { // если последний лоскут матрицы больше остальных
			int i;
			for (i = 0; i < numOfSnips - 1; i++) {
				sps[counter] = getSnipOfMatrix(matrix, numOfStartElement, snipXlength,
						snipYlengthMin, counter);
				counter++;
				
				numOfStartElement += snipXlength * snipYlengthMin;
			}
				sps[counter] = getSnipOfMatrix(matrix, numOfStartElement, snipXlength,
						snipYlengthMax, counter);
		}
		containerOfSnips.setSnips(sps);
		return containerOfSnips;
	}

	private static Snip getSnipOfMatrix(Matrix matrix, int numOfStartElement,
			int xlength, int ylength, int orderInContainer) {
		Snip snip = new Snip(numOfStartElement, xlength, ylength, orderInContainer);
		int numOfElements = xlength * ylength;
		int start = numOfStartElement;
		int end = numOfStartElement + numOfElements - 1;
		snip.setValues(getCopyOfPartOfDoubleArray(matrix.getValues(), start,
				end));
		snip.setXs(getCopyOfPartOfIntArray(matrix.getXs(), start, end));
		snip.setYs(getCopyOfPartOfIntArray(matrix.getYs(), start, end));
		return snip;
	}

	private static double[] getCopyOfPartOfDoubleArray(double[] original,
			int start, int end) {
		int size = end - start + 1;
		
		double[] res = new double[size];
		int innerCounter = 0;
		for (int i = start; i <= end; i++) {
			res[innerCounter] = original[i];
			innerCounter++;
		}
		return res;
	}

	private static int[] getCopyOfPartOfIntArray(int[] original, int start,
			int end) {
		int size = end - start + 1;
		
		int[] res = new int[size];
		
		int innerCounter = 0;
		for (int i = start; i <= end; i++) {
			res[innerCounter] = original[i];
			innerCounter++;
		}
		return res;
	}

}
